/**
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.workflow.container;

import java.util.HashMap;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cc.aileron.accessor.PojoAccessor;
import cc.aileron.workflow.WorkflowActivity;
import cc.aileron.workflow.WorkflowJudgment;
import cc.aileron.workflow.WorkflowParameterBinder;
import cc.aileron.workflow.WorkflowProcess;
import cc.aileron.workflow.activity.WorkflowActivityFactory;
import cc.aileron.workflow.container.binder.WorkflowValidatorAndProcessContainer.WorkflowValidatorAndProcess;

import com.google.inject.Provider;

/**
 * @author Aileron
 * @param <Resource>
 */
public class WorkflowExecutorImpl<Resource> implements WorkflowExecutor
{
    @Override
    public final void execute(final HashMap<String, Object> parameters)
    {
        final PojoAccessor<Resource> accessor = accessorProvider.get();
        final WorkflowActivity<Resource> activity = activityFactory.create(accessor,
                parameters);
        try
        {
            try
            {
                parameterBinder.bind(accessor, parameters, status.keys());
                doProcess(activity);
            }
            catch (final Exception e)
            {
                final WorkflowProcess<Resource> errorProcess = exceptionMappings.get(e.getClass());
                if (errorProcess != null)
                {
                    try
                    {
                        errorProcess.doProcess(activity);
                        return;
                    }
                    catch (final Exception e1)
                    {
                        throw e1;
                    }
                }
                throw e;
            }
        }
        catch (final Exception e)
        {
            logger.error("do process error", e);
            throw new Error(e);
        }
    }

    /**
     * @param activity
     * @throws Exception
     */
    private void doProcess(final WorkflowActivity<Resource> activity)
            throws Exception
    {
        for (final WorkflowValidatorAndProcess<Resource> vp : processList)
        {
            final WorkflowProcess<Resource> process = vp.process();
            final WorkflowJudgment<Resource> validator = vp.validator();
            if (validator == null)
            {
                logger.trace("doProcess#process : {}", process);
                process.doProcess(activity);
                continue;
            }

            logger.trace("doProcess#validator : {}", validator);
            if (validator.doJudgment(activity))
            {
                continue;
            }

            logger.trace("doProcess#errorProcess : {}", process);
            process.doProcess(activity);
            return;
        }
    }

    /**
     * constractor injection
     * 
     * @param activityFactory
     * @param accessorProvider
     * @param status
     */
    public WorkflowExecutorImpl(final WorkflowActivityFactory activityFactory,
            final Provider<PojoAccessor<Resource>> accessorProvider,
            final WorkflowExecutorStatus<Resource> status)
    {
        this.accessorProvider = accessorProvider;
        this.activityFactory = activityFactory;
        this.status = status;
        this.parameterBinder = status.parameterBinderContainer.get();
        this.exceptionMappings = status.exceptionMappings;
        this.processList = status.processList;
    }

    private final Provider<PojoAccessor<Resource>> accessorProvider;
    private final WorkflowActivityFactory activityFactory;
    private final HashMap<Class<Exception>, WorkflowProcess<Resource>> exceptionMappings;
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final WorkflowParameterBinder parameterBinder;
    private final List<WorkflowValidatorAndProcess<Resource>> processList;
    private final WorkflowExecutorStatus<Resource> status;
}